local skynet = require "skynet"
local socket = require "skynet.socket"
local cjson = require "cjson"
local netpack = require "skynet.netpack"
-- local parser = require "parser"
-- local protobuf = require "protobuf"
-- local Message = require ".robotsvr.Message"
local MessagePack = require "MessagePack"
-- local ProtoLoader = require "ProtoLoader"
local config = require "configquery"

local Network = class("Network")

function Network:ctor(ip,port)
    self.fd = nil
    self.lastPackage = ""

    -- self.protobuf = nil
    self.tbSelfMsg = {}
    self.ip = ip
    self.port = port
    self.recv_fork = nil
    self.message_pack = MessagePack.new()


    -- self:open(ip,port)
end

function Network:init(pbc_env)    
    self.message_pack:initProto(pbc_env)
end

---------------------------------------------------------
-- private
---------------------------------------------------------
--取pid
function Network:readPid( text ) --big endian
    return text:byte(5) * 256 + text:byte(6) 
end

function Network:readExt( text ) --big endian
    return text:byte(3) * 256 + text:byte(4) 
end

function Network:readShortLittle( text ) --little endian
    return text:byte(1) + text:byte(2) * 256
end

--取大端2个字节
function Network:readShortBig( text ) --big endian
    return text:byte(1)*256 + text:byte(2)
end

--包长度2字节
function Network:unpackPackage(text)
    local size = #text
    if size < 2 then
        return nil, text
    end
    local pack_size = self:readShortBig(text)
    if size < pack_size+2 then
        return nil, text
    end
    local offset = 2
    local msg = text:sub(offset + 1, offset+pack_size)
    local last = text:sub(offset + 1 + pack_size)
    return msg, last
end

--解消息
function Network:decodeMsg( msgbuf )
    local size = #msgbuf
    if size<4 then return false end
    -- msgbuf = self:decryptData(msgbuf)

    -- local sid = self:readShortBig(msgbuf)
    -- local ext = self:readExt(msgbuf)
    -- local pack_id = self:readPid(msgbuf)
    -- local pack_name = pack_helper.get_pack(pack_id)
    -- if not pack_name then
    --     print('decodeMsg no found pack_id:'..pack_id)
    --     return
    -- end
    -- local  pack_body = msgbuf:sub(5)

    -- local ok, pack_body = pcall(self.protobuf.decode, pack_name, pack_body, #pack_body)
    -- if not ok then
    --     print('decodeMsg msgbody invalid package', pack_body)
    -- end
    -- print("_____decodeMsg______")
-- local message = skynet.tostring(msgbuf, #msgbuf)  
    local pack_name, message_body = self.message_pack:unpackClient(msgbuf)
    return pack_name, message_body
end


--组装json包
function Network:encodeJsonMsg(pack_id, msg_body)
    local msgbody
    if next(msg_body) then
        msgbody = cjson.encode(msg_body)
        if not msgbody then
            log.error('encode json msg body failed')
            return
        end
    else
        msgbody = ''
    end
    --http://cloudwu.github.io/lua53doc/manual.html#6.4.2
    local msg = string.pack(">H", pack_id)..msgbody
    if msg == nil then
        log.error('msg pack failed'..pack_id)
        return
    end
    return msg
end

--连接socket
function Network:open(ip, port)
    self.ip = ip or self.ip
    self.port = port or self.port
    self.fd = socket.open(self.ip, self.port)
    if not self.fd then
        print("连接 ",ip,":",port,"失败")
        return
    end
    self.recv_fork = skynet.fork(function() self:recvLoop() end)
    print("########连接"..self.ip..":"..self.port.."成功#########")
    local func = self.tbSelfMsg["connect"]
    -- print("______func__",self.tbSelfMsg)
    if func then 
        func("connect")
    end
end

function Network:close()
    print("____________Network close__________",self.fd)
    if self.fd then 
        socket.close(self.fd)
    end
    self.fd = nil
    if self.recv_fork then
        print("____111_______resume_______")
        coroutine.resume(self.recv_fork)
    end
    self.lastPackage = ""
    -- self:dispatchMessage("socket_close")
    -- skynet.exit()
end

function Network:redirect(ip, port)
    --local handler = self.handler
    self:close()
    self:open(ip, port)
    if not self.fd then
        print("连接 "..ip..":"..port.."失败")
        return false
    end
    --self.handler = handler
    print("连接"..ip..":"..port.."成功")
    return true
end

--接收消息
function Network:recvLoop()
    while true do
        --print(">>>M:recv_loop>>>")
        if not self.fd then
            print("接收数据协程结束")
            return
        end
        local str = socket.read(self.fd)
        if str then
            self:receive(str)
        else
            print("接收数据错误协程结束")
            self:close()
            return
        end
    end
end

function Network:receive(str)
    -- print("收到数据"..#str)
    self.lastPackage = self.lastPackage .. str
    local msg
    local command
    while #self.lastPackage > 0 do
        msg, self.lastPackage = self:unpackPackage(self.lastPackage)
        if not msg then break end
        local ok ,  pack_body = pcall(handler(self,self.decodeMsg), msg)        
        if not ok then
            print("message dispatch error: "..tostring(pack_body))
        else
            -- print("____receive msg ___",pack_body)
            command = pack_body.command
            self:dispatchMessage(command, pack_body.message_body)               
           
        end
    end
end

--socket写入
function Network:writePackage(msg)
    if not self.fd or not msg then
        print("##########writePackage error######")
        return false
    end
    local tmpmsg, sz = netpack.pack(msg)
    local ok,err = pcall(socket.write, self.fd, tmpmsg, sz)
    if not ok then
        print('error#########send_msgto_remote faild:',err,self.fd,msg)       
        return false
    end
    return true
end

---------------------------------------------------------
-- public
---------------------------------------------------------

--发送
function Network:send(main_id, sub_id, body)
    if main_id~=6001 and sub_id~=3 then 
        print("________send___",main_id, sub_id, config["message_map"][main_id][sub_id])
    end
    local str = self.message_pack:packClient(main_id, sub_id, body)
    self:writePackage(str)

end

--发送josn
function Network:sendJson(data)
    for k,v in pairs(data) do 
        local pack =  10000
        if not pack then
            print('send message faild no found msg_name:',msg_name,k)
            return
        end
        local msg = self:encodeJsonMsg(pack, v)
        print("___",msg)
        -- msg = self:encryptData(msg)
        if msg then
            return self:writePackage(msg)
        else
            print('write_package faild :',k)
        end
    end    
end

--注册消息回调
function Network:register(name,callback)
    print("_________register____",name, callback)
    self.tbSelfMsg[name] = callback
end

--消息派发
function Network:dispatchMessage(command, ... )
    -- local cmd = ""..command.main_cmd_id..command.sub_cmd_id
    local cmd = config["message_map"][command.main_cmd_id][command.sub_cmd_id]
    local func = self.tbSelfMsg[cmd]
    if not func then 
        --log.error("####### cmd "..cmd .." not found at robot "..self.svrName)
        print("########### Network dispatchMessage not found message func ",cmd)
        return
    end
    -- print("_______recv__",...)
    local ok, res = x_pcall(func,...)
    if not ok then 
        print(res)
        print("____可能__没注册函数___",cmd,command.main_cmd_id,command.sub_cmd_id, func)
        -- print("___config__",config["message_map"][command.main_cmd_id], config["message_map"][command.main_cmd_id][command.sub_cmd_id])
    else
        -- print("______函数_ok__",cmd,command.main_cmd_id,command.sub_cmd_id)
    end
end


return Network
